#include <cstdio>
#include <cstdlib>

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>

#include <functional>
#include <memory>

#include "Server.hpp"
#include "protocol.hpp"
#include "Comm.hpp"
#include "daemon.hpp"
#include "protocol.hpp"
#include "Calculate.hpp"
#include "Socket.hpp"

#define BUFFERSIZE 1024
using task_t = std::function<void(Socket *)>;

void Function(Socket *sk)
{
    while (true)
    {
        // 0. 预备工作
        std::unique_ptr<Factory> factory = std::make_unique<Factory>();
        std::unique_ptr<Request> req = std::make_unique<Request>();
        std::unique_ptr<Response> resp = std::make_unique<Response>();

        // 1. server端拿到数据 : "len\nx op y\n"
        std::string datagram;
        std::string new_datagram;
        sk->Recv(new_datagram);
        datagram = new_datagram;

        // 2. 去掉报文边界: "len\nx op y\n" -> "x op y"
        if(!DelMargins(new_datagram, datagram)) continue;

        // 3. 反序列化 : "x op y" -> {x, op, y}
        req->Deserialize(new_datagram);

        // 4. 处理数据
        Calculate cal(req->GetData_X(), req->GetData_Y(), req->GetOper());
        resp = cal.Compute();

        // 5. 将结果序列化 : {result, stat} -> "res stat"
        std::string message = resp->Serialize();

        // 6. 将序列化结果添加报文边界 "res stat" -> "len\nres stat\n"
        AddMargins(message);

        // 7. 发送数据
        sk->Send(message);
    }
    close(sk->GetSockfd());
}

int main(int argc, const char *argv[])
{
    // 判断是否输入了端口号，端口号是表示本机唯一进行本次网络通信的进程
    if (argc != 2)
    {
        printf("Usage:\n\t%s server_port\n\n", argv[0]);
        exit(0);
    }

    // 将server变成守护进程，从此不会被用户的登录和退出所影响
    // Daemon(false, false);

    // 拿端口号
    in_port_t server_port = std::stoi(argv[1]);

    // 初始化server，并交付其内部线程执行的IO方法
    std::unique_ptr<Server<task_t>> server = std::make_unique<Server<task_t>>(server_port,
                                                                              std::bind(Function, std::placeholders::_1));
    // 这里使用占位符，就要求我们再调用该函数的时候，要自己传入参数
    // 主线程进行accept， 线程池中执行Function方法-》IO
    server->Running();

    return 0;
}